#include "SelectionBase.hxx"

ClassImp(SelectionBase)

//********************************************************************
SelectionBase::SelectionBase(bool forceBreak){
//********************************************************************

    _name="";
    _enabled=true;  
    
    // maximum accum cut level for the current event 
    _max_accum_cut_level = 0;
    
    _branchSequence.clear();
    _branchAlias.clear();
    _nEventsPassed.clear();
    
    for(unsigned int cut = 0; cut < NMAXSTEPS; ++cut){
      _cut_passed_stats[0][cut] = 0;
    }
    
    
    _nCutsInBranch.clear();
    
    //  AddBranch(0, ConvertBranch(),"trunk");
    
    // whether to break or not the cut sequence when a cut is not passed
    _forceBreak = forceBreak;
    
    /// By default EventSummary class is created and filled when the selection is passed. But this might be necessary also for events not passing the selection
    /// The user can force the EventSummary to be filled always. 
    _forceFillEventSummary = false;

    // Initialised all run periods to be true - the selection will be applied to all run periods by default
    // Really should use enumeration for run periods!!
    for(int i = 0; i < 10; ++i){
        _validRunPeriods[i] = true;
    }


}

//********************************************************************
bool SelectionBase::Apply(AnaEventB& event){
//********************************************************************

    //------ This function is called for each buch and each virtual analysis (toy experiment) ----------

    // Create the appropriate AnaBoxB type to store intermediate objects
    AnaBoxB* box = MakeBox();

    bool ok = 0;
    // If we're multithreading we cannot change selection member variables, so statistics don't work
    // Need to pass array to selection instead to calculate which branch the event passed
#ifdef MULTITHREAD
    /// The accumulated cut level - the maximum cut number for which all previous
    /// cuts were also passed.
    Int_t all_cuts_passed[NMAXBRANCHES];

    for(UInt_t i = 0; i < NMAXBRANCHES; ++i){
        all_cuts_passed[i] = 1;
    }

    // Apply all steps. Returns true when all cuts are passed
    ok = Apply(event,*box, all_cuts_passed);
#else
    // Apply all steps. Returns true when all cuts are passed
    ok = Apply(event,*box); 
#endif
    // delete the analysis box
    delete box;

    return ok;
}

//********************************************************************
bool SelectionBase::Apply(AnaEventB& event, AnaBoxB& box){
//********************************************************************

    //------ This function is called for each buch and each virtual analysis (toy experiment) ----------

    // Apply all steps. Returns true when all cuts are passed
    bool ok = ApplySteps(event,box); 

    //The above is not branch aware.  So, we loop over all the branches
    //seeing if allCutsPass is true for at least one of them.
    for (UInt_t j=0;j<GetNBranches();j++){
        if(_allCutsPassed[j]){
            ok = true;
            break;
        }
    } 

    /// By default EventSummary class is created and filled when the selection is passed. But this might be necessary also for events not passing the selection
    /// The user can force the EventSummary to be filled always by setting _forceFillEventSummary to true. 
    if (ok || _forceFillEventSummary){
        // Fill the summary event properties for a succesful selection
        FillEventSummary(box, event);
    }

    return ok;
}

//********************************************************************
bool SelectionBase::Apply(AnaEventB& event, AnaBoxB& box, Int_t all_cuts_passed[]){
//********************************************************************

    //------ This function is called for each buch and each virtual analysis (toy experiment) ----------

    // Apply all steps
    ApplySteps(event,box, all_cuts_passed); 

    // Tell the FillEventSummary which cuts were passed
    bool ok = FillEventSummary(box, event, all_cuts_passed);

    return ok;
}

//********************************************************************
void SelectionBase::FillEventSummary(const AnaBoxB& box, AnaEventB& event){
//********************************************************************

    FillEventSummary(box,event,_allCutsPassed);
}

//********************************************************************
void SelectionBase::PrintStatistics() const{
//********************************************************************

    std::cout << "----------- Statistics for selection '" << _name << "'" << std::endl;

    UInt_t ID=0;
    for (std::vector<std::string>::const_iterator it = _branchAlias.begin();it!=_branchAlias.end();it++, ID++){
        PrintStatistics(ID);
    }
}

//********************************************************************
void SelectionBase::PrintStatistics(UInt_t ID) const{
//********************************************************************

    std::cout << "branch '" << GetBranchAlias(ID) << "' --> # events = " << _nEventsPassed[ID] << std::endl;
    for(unsigned int cut = 0; cut < NMAXSTEPS; ++cut){
        std::cout << "cut " << cut << " = " << _cut_passed_stats[ID][cut] << std::endl;
    }

}

//********************************************************************
void SelectionBase::PrintStatistics(const std::string& name) const{
//********************************************************************

    UInt_t ID = GetBranchUniqueID(name);
    PrintStatistics(ID);
}

//********************************************************************
void SelectionBase::InitializeCutLevels(){
//********************************************************************

    int nBranches = GetNBranches();

    memset(&_accum_cut_level[0], 0, nBranches*sizeof(_accum_cut_level[0]));
    memset(&_current_cut[0],     0, nBranches*sizeof(_current_cut[0]));
    memset(&_cut_passed[0][0],   0, sizeof(_cut_passed[0][0])*nBranches*NMAXSTEPS);

    for (int i=0;i<nBranches;i++)
      _allCutsPassed[i] = 1;

    // Reset the maximum accum_cut_level from all branches
    _max_accum_cut_level = 0;

    //
    //  for (UInt_t j=0;j<GetNBranches();j++){
    //    _allCutsPassed[j]=true;    
    //    _accum_cut_level[j]=0;    
    //    _current_cut[j]=0;
    //
    //    for (UInt_t i=0;i<NMAXSTEPS;i++)
    //      _cut_passed[j][i]=false;
    //  }
}

//********************************************************************
bool SelectionBase::ApplyStep(const AnaEventB& event, AnaBoxB& box,const StepBase& step, Int_t branch) {
//********************************************************************

    bool passed = step.Apply(event, box);  
    // If the step is not a cut it should return always true
    if (step.Type() != StepBase::kCut){
        CutPassed(passed, branch);
        return true;
    }

    return passed;
}

//********************************************************************
bool SelectionBase::ApplyStepRecursive(const AnaEventB& event, AnaBoxB& box,const StepBase& step) {
//********************************************************************

    // This function applies all steps recursively provided the first step

    // Apply the current step
    // If the step is not a cut it should return always true
    bool passed = step.Apply(event, box);  

    // Deal with accum_level and individual steps passed
    if (step.Type() == StepBase::kCut){
        CutPassed(passed, step);

        // Stop applying steps when there is a break point.
        // Or when _forceBreak == true (we are not interested about intermediate steps)
        if (!passed && (step.Break() || _forceBreak)) return false;
    }    

    for (UInt_t i = 0; i < step.GetNextSteps().size(); ++i){
        StepBase* it=step.GetNextSteps()[i];
        bool passed2 = ApplyStepRecursive(event,box,*it);
        // When a cut is not passed the final return must be false
        if (passed) passed=passed2;
    }

    return passed;
}

//********************************************************************
bool SelectionBase::ApplyStepRecursive(const AnaEventB& event, AnaBoxB& box,const StepBase& step, Int_t all_cuts_passed[]) {
//********************************************************************

    // This function applies all steps recursively provided the first step

    // Apply the current step
    // If the step is not a cut it should return always true
    bool passed = step.Apply(event, box);  

    // Deal with accum_level and individual steps passed
    if (step.Type() == StepBase::kCut){
        CutPassed(passed, step, all_cuts_passed);

        // Stop applying steps when there is a break point.
        // Or when _forceBreak == true (we are not interested about intermediate steps)
        if (!passed && (step.Break() || _forceBreak)) return false;
    }    

    for (UInt_t i = 0; i < step.GetNextSteps().size(); ++i){
        StepBase* it=step.GetNextSteps()[i];
        bool passed2 = ApplyStepRecursive(event,box,*it, all_cuts_passed);
        // When a cut is not passed the final return must be false
        if (passed) passed=passed2;
    }

    return passed;
}



//********************************************************************
bool SelectionBase::ApplySteps(const AnaEventB& event, AnaBoxB& box, const std::string& branch, Int_t ifirst, Int_t ilast) {
//********************************************************************

    // This function applies steps in a given branch from step ifirst to step ilast

    // initialize the cut levels
    InitializeCutLevels();

    bool all_passed=true;

    // Get all steps in a given branch
    std::vector<StepBase*> steps = GetStepsInBranch(branch);

    // Get the branch unique ID for a given branch name
    UInt_t ID = GetBranchUniqueID(branch);

    // Loop over steps
    Int_t ilast2 = std::min(ilast,(Int_t)steps.size());
    for (Int_t i=ifirst;i<ilast2;i++ ){
        bool passed = ApplyStep(event, box, *steps[i], ID);
        if (!passed) all_passed=false;
        // Don't continue when a given step(cut) has a break point
        // Or when _forceBreak == true (we are not interested about intermediate steps)
        if ((steps[i]->Break() || _forceBreak) && !passed) break; 
    }

    if (all_passed)
        _nEventsPassed[ID]++;

    // TODO. This return value is not really used (yet !!!!)
    return all_passed;
}

//********************************************************************
bool SelectionBase::ApplySteps(const AnaEventB& event, AnaBoxB& box) {
//********************************************************************

    // This function applies all steps in all branches

    // There should be at least one step
    if ( _firstSteps.size() == 0) return false;

    // initialize the cut levels
    InitializeCutLevels();

    // Apply all steps recursively
    bool ok = ApplyStepRecursive(event, box, **_firstSteps.begin());

    // update the event selection statistics
    for (UInt_t i=0; i<_nEventsPassed.size();i++){
        if (_allCutsPassed[i]) _nEventsPassed[i]++;
    }

    return ok;
}

//********************************************************************
bool SelectionBase::ApplySteps(const AnaEventB& event, AnaBoxB& box, Int_t all_cuts_passed[]) {
//********************************************************************

    // This function applies all steps in all branches

    // There should be at least one step
    if ( _firstSteps.size() == 0) return false;

    // Apply all steps recursively
    bool ok = ApplyStepRecursive(event, box, **_firstSteps.begin(), all_cuts_passed);

    return ok;
}

//********************************************************************
bool SelectionBase::CutPassed(bool ok, const StepBase& step, Int_t all_cuts_passed[]) {
//********************************************************************

    // if the step is not a cut it's allways passed
    if (step.Type() != StepBase::kCut) return true;

    // Get the list of branches unique IDs associated to this cut
    const std::vector<UInt_t>& branches = step.GetBranchUniqueIDs();

    // Process each of the branches
    for (UInt_t i = 0;i<branches.size();i++){
        if (!ok) all_cuts_passed[branches[i]] = false;
    }

    return ok;
}

//********************************************************************
bool SelectionBase::CutPassed(bool ok, const StepBase& step) {
//********************************************************************

    // if the step is not a cut it's allways passed
    if (step.Type() != StepBase::kCut) return true;

    // Get the list of branches unique IDs associated to this cut
    const std::vector<UInt_t>& branches = step.GetBranchUniqueIDs();

    // Process each of the branches
    for (UInt_t i = 0;i<branches.size();i++){
        CutPassed(ok, branches[i]);
    }

    return ok;
}

//********************************************************************
bool SelectionBase::CutPassed(bool ok, Int_t branch) {
//********************************************************************

    if (ok) 
        CutPassed(_current_cut[branch],branch);    
    else 
        _allCutsPassed[branch] = false;

    // increment the current cut in each branch
    _current_cut[branch]++;

    return ok;
}

//********************************************************************
void SelectionBase::CutPassed(Int_t icut, Int_t branch) {
//********************************************************************

    // Compute the accum level for each branch

    // the event has passed cut number "icut"
    _cut_passed[branch][icut]=true;
    _cut_passed_stats[branch][icut]++;

    // The event has passed all previous cuts and this one
    if (_accum_cut_level[branch]==icut || icut==0){
        _accum_cut_level[branch]++;

        // compute the max accum cut level from all branches
        if (_accum_cut_level[branch]> _max_accum_cut_level) 
            _max_accum_cut_level = _accum_cut_level[branch];
    }
}

//********************************************************************
UInt_t SelectionBase::GetNMaxCuts(){
//********************************************************************

    /// Return the number of cuts that have been added.

    UInt_t nmaxcuts=0;
    for (UInt_t i=0;i<GetNBranches();i++ )
        if ( GetNCuts(i) > nmaxcuts) nmaxcuts=GetNCuts(i);

    return nmaxcuts;
}

//********************************************************************
UInt_t SelectionBase::GetNCuts(Int_t Branch){
//********************************************************************

    /// Return the number of cuts that have been added for branch 'Branch'
    if (Branch >= (Int_t)GetNBranches() || Branch<0) return 0;
    return _nCutsInBranch[Branch];
}

//********************************************************************
void SelectionBase::DumpSteps(const std::string& name, bool onlycuts) {
//********************************************************************

    if (name==""){
        std::vector<std::string>::iterator it;
        for (it=_branchAlias.begin();it!=_branchAlias.end();it++)
            DumpSteps(*it,onlycuts);
    }
    else{
        Int_t ID = GetBranchUniqueID(name);
        DumpSteps(ID,onlycuts);
    }
}

//********************************************************************
void SelectionBase::DumpSteps(Int_t ID, bool onlycuts) {
//********************************************************************

    if (ID==-1){
        std::vector<std::string>::iterator it;
        for (it=_branchAlias.begin();it!=_branchAlias.end();it++)
            DumpSteps(*it,onlycuts);
    }
    else{
      if (ID >= (Int_t)GetNBranches() || ID<0){ 
	  std::cout << "SelectionBase::DumpSteps(). Branch with index " << ID << " does not exist in selection '"<< _name <<"' !!!" << std::endl; 
	  return;
	}

        std::vector<UInt_t> branch = GetBranchSequence(ID);
        std::string alias = GetBranchAlias(ID);

        std::cout << "-------------------------------------------------------------------" << std::endl;
        std::cout << "      Steps for selection '" << _name << "' branch " << ConvertBranchToString(branch) << " '" << alias << "'" << std::endl;
        std::cout << "-------------------------------------------------------------------" << std::endl;

        char out[256];  
        sprintf(out,"%3s: %-10s %-25s %-6s %-10s", "#", "type",  "title", "break", "branches");
        std::cout << out << std::endl;
        std::cout << "-------------------------------------------------------------------" << std::endl;

        std::vector<StepBase*> steps = GetStepsInBranch(branch);
        UInt_t step=0;
        for (UInt_t i=0;i<steps.size();i++ ){
            if (!onlycuts || steps[i]->Type() == StepBase::kCut){      
                sprintf(out,"%3d: %-10s %-25s %-6d", step, steps[i]->ConvertType().c_str(),  steps[i]->Title().c_str(), (int)steps[i]->Break());
                std::cout << out;
                const std::vector<UInt_t>& IDs = steps[i]->GetBranchUniqueIDs();
                for (UInt_t j=0;j<IDs.size();j++){
                    std::cout << IDs[j] << " ";
                }
                std::cout << std::endl;
                step++;
            }

            if (steps[i]->GetNextSteps().size()>1) 
                std::cout << "--- split with " << steps[i]->GetNextSteps().size() << " branches" << std::endl;
        }
        std::cout << "-------------------------------------------------------------------" << std::endl;

    }
}

//********************************************************************
void SelectionBase::AddStep(Int_t b0, Int_t b1, Int_t b2, Int_t b3, Int_t b4, Int_t b5, Int_t b6, Int_t b7, StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break){
//********************************************************************
    std::vector<UInt_t> branch = ConvertBranch(b0,b1,b2,b3,b4,b5,b6,b7);
    AddStep(branch, type, title, step, cut_break);
}

//********************************************************************
void SelectionBase::AddStep(Int_t b0, Int_t b1, Int_t b2, Int_t b3, Int_t b4, Int_t b5, Int_t b6, StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break){
//********************************************************************
    std::vector<UInt_t> branch = ConvertBranch(b0,b1,b2,b3,b4,b5,b6);
    AddStep(branch, type, title, step, cut_break);
}

//********************************************************************
void SelectionBase::AddStep(Int_t b0, Int_t b1, Int_t b2, Int_t b3, Int_t b4, Int_t b5, StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break){
//********************************************************************
    std::vector<UInt_t> branch = ConvertBranch(b0,b1,b2,b3,b4,b5);
    AddStep(branch, type, title, step, cut_break);
}

//********************************************************************
void SelectionBase::AddStep(Int_t b0, Int_t b1, Int_t b2, Int_t b3, Int_t b4, StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break){
//********************************************************************
    std::vector<UInt_t> branch = ConvertBranch(b0,b1,b2,b3,b4);
    AddStep(branch, type, title, step, cut_break);
}

//********************************************************************
void SelectionBase::AddStep(Int_t b0, Int_t b1, Int_t b2, Int_t b3, StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break){
//********************************************************************
    std::vector<UInt_t> branch = ConvertBranch(b0,b1,b2,b3);
    AddStep(branch, type, title, step, cut_break);
}

//********************************************************************
void SelectionBase::AddStep(Int_t b0, Int_t b1, Int_t b2, StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break){
//********************************************************************
    std::vector<UInt_t> branch = ConvertBranch(b0,b1,b2);
    AddStep(branch, type, title, step, cut_break);
}

//********************************************************************
void SelectionBase::AddStep(Int_t b0, Int_t b1, StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break){
//********************************************************************
    std::vector<UInt_t> branch = ConvertBranch(b0,b1);
    AddStep(branch, type, title, step, cut_break);
}

//********************************************************************
void SelectionBase::AddStep(Int_t b0, StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break){
//********************************************************************
    std::vector<UInt_t> branch = ConvertBranch(b0);
    AddStep(branch, type, title, step, cut_break);
}

//********************************************************************
void SelectionBase::AddStep(StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break){
//********************************************************************
    std::vector<UInt_t> branch = ConvertBranch();
    AddStep(branch, type, title, step, cut_break);
}

//********************************************************************
void SelectionBase::AddStep(const std::vector<UInt_t>& branch, StepBase::TypeEnum type, const std::string& title, StepBase* step, bool cut_break){
//********************************************************************

    // Set the type (cut or action)
    step->SetType(type);

    // Set the title (a nice name to be displayed in plots)
    step->SetTitle(title);

    // Set the cut break (only for cuts)
    if (step->Type() == StepBase::kCut)
        step->SetBreak(cut_break);

    // Add the step
    AddStep(branch, step);  
}

//********************************************************************
void SelectionBase::AddStep(const std::vector<UInt_t>& branch, StepBase* step){
//********************************************************************

    if (_firstSteps.size() ==0){
        // Add the first step
        _firstSteps.push_back(step);
    }
    else if (_firstSteps.size() >1 && !_firstSteps[branch[0]]){
        _firstSteps[branch[0]] = step;
    }
    else{
        // Get the last step added to this branch sequence
        StepBase* laststep = GetLastStepInBranch(branch);        

        if (!laststep) return;
        if (branch.size()==0)   
            // Add the step to trunk
            laststep->AddNextStep(step);  
        else  {
            // Add the step to branch
            laststep->AddNextStep(step, branch[branch.size()-1]);  
        }
    }

}

//********************************************************************
void SelectionBase::AddSteps(const std::vector<UInt_t>& branch, const std::vector<StepBase*>& steps){
//********************************************************************

    for (std::vector<StepBase*>::const_iterator it= steps.begin(); it!=steps.end();it++){
        AddStep(branch, *it);
    }
}

//********************************************************************
void SelectionBase::AddSplit(UInt_t nbranches, Int_t b0, Int_t b1, Int_t b2, Int_t b3, Int_t b4, Int_t b5, Int_t b6, Int_t b7){
//********************************************************************

    std::vector<UInt_t> branch = ConvertBranch(b0,b1,b2,b3,b4,b5,b6,b7);
    AddSplit(nbranches,branch);
}

//********************************************************************
void SelectionBase::AddSplit(UInt_t nbranches, const std::vector<UInt_t>& branch_seq){
//********************************************************************

    std::cout << "SelectionBase::AddSplit(). Add split with " << nbranches << " branches to branch "<< ConvertBranchToString(branch_seq) << std::endl;


    if (_firstSteps.size() ==0){
        // Add "nbranches" NULL steps to the last step

        //    AddStep(StepBase::kAction, "dummy", new StepBase(), false);  
        AddSplit(nbranches,branch_seq);
    }


    // Get the last step in the branch sequence
    StepBase* laststep = GetLastStepInBranch(branch_seq);        
    if (!laststep) return;

    if (laststep->GetNextSteps().size() !=0){
        std::cout << "SelectionBase::AddSplit(). Last step in branch " << ConvertBranchToString(branch_seq) << " with title '" <<  laststep->Title() << "'" 
            << " already has an split with " << laststep->GetNextSteps().size() << " branches !!!!" << std::endl;
        return;
    }
    /*
       if (branch_seq.size()==0){
       _branchSequence.clear();
       _branchAlias.clear();
       _nEventsPassed.clear();
       _allCutsPassed.clear();
       }
       */
    // Add "nbranches" NULL steps to the last step
    for (UInt_t i=0;i<nbranches;i++){
        laststep->AddNextStep(NULL);  
        // Add a branch, but only when this is a first level split
        //    AddBranch(i, branch_seq);
    }
}

//********************************************************************
std::vector<StepBase*> SelectionBase::GetStepsInBranch(Int_t b0, Int_t b1, Int_t b2, Int_t b3, Int_t b4, Int_t b5, Int_t b6, Int_t b7){
//********************************************************************

    std::vector<UInt_t> branch = ConvertBranch(b0,b1,b2,b3,b4,b5,b6,b7);
    return GetStepsInBranch(branch);
}

//********************************************************************
std::vector<StepBase*> SelectionBase::GetStepsInBranch(const std::vector<UInt_t>& branch, UInt_t first, UInt_t last){
//********************************************************************

    std::vector<StepBase*> steps = GetStepsInBranchWithDummy(branch, first, last);  
    if ((*steps.begin())->Title()=="dummy")
        steps.erase(steps.begin());
    return steps;
}

//********************************************************************
std::vector<StepBase*> SelectionBase::GetStepsInBranchWithDummy(const std::vector<UInt_t>& branch, UInt_t first, UInt_t last){
//********************************************************************

    std::vector<StepBase*> steps;

    // three different cases

    // 1. No steps were added yet, return an empty vector
    if ( _firstSteps.size() ==0) return steps;

    // Get all steps from trunk (up to first split)
    if ( _firstSteps.size() == 1){ 
        steps.push_back(*_firstSteps.begin());
        if (*steps.rbegin()) 
            (*steps.rbegin())->GetNextSteps(steps);  
    }

    // 2. There is one split at the beginning of the step sequence

    bool ok=true;
    for (UInt_t i=0;i<branch.size();i++){
        if (i==0 && _firstSteps.size() > 1){
            steps.push_back(_firstSteps[branch[0]]);
            if (*steps.rbegin()) ok = (*steps.rbegin())->GetNextSteps(steps,branch[i]);  // Get all steps up to (i+1)th split 
            ok = true;
        }    
        else    if (ok && *steps.rbegin()) ok = (*steps.rbegin())->GetNextSteps(steps,branch[i]);  // Get all steps up to (i+1)th split 
    }
    if (!ok){
        std::cout << "SelectionBase::GetStepInBranch(). Branch " << ConvertBranchToString(branch) << " does not exist !!!" << std::endl;
        steps.clear(); 
    }

    if (first==0 && last==NMAXSTEPS-1){
        // Return all steps in this branch
        return steps;
    }
    else{
        // Return only from step "first" to step "last"
        std::vector<StepBase*> steps2;
        if (first>=steps.size()){
            std::cout << "SelectionBase::GetStepsInBranch(). Invalid first step " << first << ", there are " << steps.size() << " steps !!!" << std::endl;
            return steps2;
        }
        if (last>=steps.size()){
            std::cout << "SelectionBase::GetStepsInBranch(). Invalid last step " << last << ", there are " << steps.size() << " steps !!!" << std::endl;
            return steps2;
        }
        if (last<first){
            std::cout << "SelectionBase::GetStepsInBranch(). Last step " << last << " smaller than first step " << first << " !!!" << std::endl;
            return steps2;
        }

        for (UInt_t i=first;i<=last;i++)
            steps2.push_back(steps[i]);
        return steps2;
    }
}

//********************************************************************
std::vector<StepBase*> SelectionBase::GetStepsInBranch(const std::string& name, UInt_t first, UInt_t last){
//********************************************************************

    std::vector<UInt_t> branch = GetBranchSequence(name);
    return GetStepsInBranch(branch,first,last);
}

//********************************************************************
std::vector<StepBase*> SelectionBase::GetStepsInBranch(UInt_t ID, UInt_t first, UInt_t last){
//********************************************************************

    std::vector<UInt_t> branch = GetBranchSequence(ID);
    return GetStepsInBranch(branch,first,last);
}

//********************************************************************
std::vector<StepBase*> SelectionBase::GetCutsInBranch(UInt_t ID){
//********************************************************************

    std::vector<StepBase*> cuts;
    std::vector<StepBase*> steps = GetStepsInBranch(ID);
    for (std::vector<StepBase*>::iterator it=steps.begin();it!=steps.end();it++){
        if ((*it)->Type() == StepBase::kCut) cuts.push_back(*it);
    }

    return cuts;
}

//********************************************************************
std::vector<StepBase*> SelectionBase::GetCutsInBranch(const std::string& name){
//********************************************************************

    UInt_t ID = GetBranchUniqueID(name);
    return GetCutsInBranch(ID);
}

//********************************************************************
StepBase* SelectionBase::GetLastStepInBranch(const std::vector<UInt_t>& branch){
//********************************************************************

    // get the vector of steps in this branch sequence
    std::vector<StepBase*> steps = GetStepsInBranchWithDummy(branch);

    // returns the last step if exists
    if (steps.size()!=0)
        return *(steps.rbegin());
    else{
        return NULL;
    }
}

//********************************************************************
StepBase* SelectionBase::GetLastStepInBranch(Int_t b0, Int_t b1, Int_t b2, Int_t b3, Int_t b4, Int_t b5, Int_t b6, Int_t b7){
//********************************************************************

    std::vector<UInt_t> branch = ConvertBranch(b0,b1,b2,b3,b4,b5,b6,b7);
    return GetLastStepInBranch(branch);
}

//********************************************************************
StepBase* SelectionBase::GetLastStepInBranch(const std::string& name){
//********************************************************************

    std::vector<UInt_t> branch = GetBranchSequence(name);
    return GetLastStepInBranch(branch);
}

//********************************************************************
std::vector<UInt_t> SelectionBase::ConvertBranch(Int_t b0, Int_t b1, Int_t b2, Int_t b3, Int_t b4, Int_t b5, Int_t b6, Int_t b7){
//********************************************************************

    std::vector<UInt_t> branch;
    if (b0>=0) branch.push_back(b0);
    if (b1>=0) branch.push_back(b1);
    if (b2>=0) branch.push_back(b2);
    if (b3>=0) branch.push_back(b3);
    if (b4>=0) branch.push_back(b4);
    if (b5>=0) branch.push_back(b5);
    if (b6>=0) branch.push_back(b6);
    if (b7>=0) branch.push_back(b7);

    return branch;
}

//********************************************************************
std::string SelectionBase::ConvertBranchToString(const std::vector<UInt_t>& branch){
//********************************************************************

    if (branch.size()==0) return "'trunk'";

    std::stringstream os;  

    os << "(";  
    for (UInt_t i=0;i<branch.size();i++){
        if (i>0) os<< ",";
        os << branch[i];
    }
    os << ")";

    return  os.str();

}

//********************************************************************
void SelectionBase::AddBranch(Int_t ibranch, const std::vector<UInt_t>& branch_seq, const std::string& alias){
//********************************************************************

    // associate the branch unique ID with the branch alias (branch1, branch2, ...)
    // This is just a default name, that can be changed later
    if (alias==""){
        char branch[50];
        sprintf(branch, "branch%d", ibranch);
        _branchAlias.push_back(branch);  
    }
    else
        _branchAlias.push_back(alias);  

    // associate the branch unique ID with the branch sequence
    _branchSequence.push_back(branch_seq);

    // Initialize the event counter for this branch
    _nEventsPassed.push_back(0);

    // get the vector of steps in this branch sequence
    std::vector<StepBase*> steps = GetStepsInBranchWithDummy(branch_seq);
    if (steps.size()==0) return;

    // Add the branch uniqueID to all steps in this branch
    AddBranchUniqueID(steps, ibranch);

    //Store the number of cuts in each branch such that we don't have to recomput it everytime
    _nCutsInBranch.push_back(GetCutsInBranch(ibranch).size());
}

//********************************************************************
void SelectionBase::SetBranchAlias(Int_t ID, const std::string& name, Int_t b0, Int_t b1, Int_t b2, Int_t b3, Int_t b4, Int_t b5, Int_t b6, Int_t b7){
//********************************************************************

    /*
       A full branch is defined by a branching sequence, that is the branch to be taken in each split. 
       But we can set a unique ID for the full branch and a name. In that way we can for example apply all steps 
       in a given branch by specifying the ID or the name. 
       */

    //  if (ID<0 || ID >= (Int_t)(_branchSequence.size())){
    if (ID<0 || ID > (Int_t)(_branchSequence.size())){
        std::cout << "SelectionBase::SetBranchAlias(). Invalid branch unique ID " << ID << " for branch with alias '"<<  name << "' . It should be " << _branchSequence.size() << std::endl;
        return;
    }

    // Convert the branch sequence with individual integers into a vector of integers 
    std::vector<UInt_t> branch_seq = ConvertBranch(b0, b1, b2, b3, b4, b5, b6, b7);

    // get the vector of steps in this branch sequence
    //  std::vector<StepBase*> steps = GetStepsInBranch(branch_seq);
    //  if (steps.size()==0) return;

    // Add the branch uniqueID to all steps in this branch
    //  AddBranchUniqueID(steps, ID);

    AddBranch(ID,branch_seq,name);

    // associate the branch unique ID with the branch alias
    _branchAlias[ID] = name;  

    // associate the branch unique ID with the branch sequence
    _branchSequence[ID] = branch_seq;


}

//********************************************************************
void SelectionBase::AddBranchUniqueID(std::vector<StepBase*>& steps, UInt_t ID){
//********************************************************************

    std::vector<StepBase*>::iterator it;
    for (it=steps.begin();it!=steps.end();it++){
        if (*it) (*it)->AddBranchUniqueID(ID);  
    }
}

//********************************************************************
std::vector<UInt_t> SelectionBase::GetBranchSequence(Int_t ID) {
//********************************************************************

    if (ID<0 || ID >= (Int_t)_branchSequence.size())
        return std::vector<UInt_t>();
    return  
        _branchSequence[ID];
}

//********************************************************************
std::vector<UInt_t> SelectionBase::GetBranchSequence(const std::string& name) {
//********************************************************************

    Int_t  ID = GetBranchUniqueID(name);
    return GetBranchSequence(ID);
}

//********************************************************************
Int_t SelectionBase::GetBranchUniqueID(const std::string& name) const{
//********************************************************************

    Int_t ID = 0;
    for (std::vector<std::string>::const_iterator it=_branchAlias.begin();it!=_branchAlias.end();it++, ID++){
        if (*it == name) return ID; 
    }

    return -1;
}

//********************************************************************
std::string SelectionBase::GetBranchAlias(Int_t ID) const{
//********************************************************************

    if (ID<0 || ID >= (Int_t)_branchAlias.size()) return "";
    return _branchAlias[ID];
}

//********************************************************************
std::vector<std::string> SelectionBase::GetCutNames(Int_t ID){
//********************************************************************

    std::vector<std::string> cut_names;
    std::vector<StepBase*> steps = GetStepsInBranch(ID);
    for (std::vector<StepBase*>::iterator it=steps.begin();it!=steps.end();it++){
        if ((*it)->Type() == StepBase::kCut) cut_names.push_back((*it)->Title());
    }
    return cut_names;
}

//********************************************************************
std::vector<std::string> SelectionBase::GetStepNames(Int_t ID){
//********************************************************************

    std::vector<std::string> step_names;
    std::vector<StepBase*> steps = GetStepsInBranch(ID);
    for (std::vector<StepBase*>::iterator it=steps.begin();it!=steps.end();it++){
        step_names.push_back((*it)->Title());
    }
    return step_names;
}


//********************************************************************
Int_t SelectionBase::GetCutNumber(const std::string& title, Int_t ID){
//********************************************************************

    Int_t icut=0;
    std::vector<StepBase*> steps = GetStepsInBranch(ID);
    for (std::vector<StepBase*>::iterator it=steps.begin();it!=steps.end();it++){
        if ((*it)->Type() == StepBase::kCut) icut ++;
        if ((*it)->Title() == title) break;
    }
    return icut;
}

//********************************************************************
void SelectionBase::CopySteps(SelectionBase& sel1, UInt_t branchID1, UInt_t first, UInt_t last, UInt_t branchID2){
//********************************************************************

    //  CopySteps(sel1, GetBranchAlias(branchID1), first, last, GetBranchAlias(branchID2));

    // Get the steps from the first selection
    std::vector<StepBase*> steps1 = sel1.GetStepsInBranch(branchID1, first, last);

    // Gets the branch sequence for the second selection
    //  std::vector<UInt_t> branch2 = GetBranchSequence(branchID2);
    std::vector<UInt_t> branch2 = ConvertBranch(branchID2);

    // Add a clone of the steps of the first selection to the second selection
    for (UInt_t i=0;i<steps1.size();i++){
        AddStep(branch2,steps1[i]->Type(), steps1[i]->Title(), steps1[i]->MakeClone(), steps1[i]->Break());  
    }

    DumpSteps();

}

//********************************************************************
void SelectionBase::CopySteps(SelectionBase& sel1, const std::string& sbranch1, UInt_t first, UInt_t last,const std::string& sbranch2){
//********************************************************************

    // Get the steps from the first selection
    std::vector<StepBase*> steps1 = sel1.GetStepsInBranch(sbranch1, first, last);

    // Gets the branch sequence for the second selection
    std::vector<UInt_t> branch2 = GetBranchSequence(sbranch2);

    if (branch2.size()>0)
        std::cout << branch2[0] << std::endl;



    // Add a clone of the steps of the first selection to the second selection
    for (UInt_t i=0;i<steps1.size();i++){
        AddStep(branch2,steps1[i]->Type(), steps1[i]->Title(), steps1[i]->MakeClone(), steps1[i]->Break());  
    }

    DumpSteps();
}

//********************************************************************
void SelectionBase::CopySteps(SelectionBase& sel1, const std::string& sbranch1, const std::string& sbranch2){
//********************************************************************

    // Copy all steps
    std::vector<StepBase*> steps1 = sel1.GetStepsInBranch(sbranch1);
    CopySteps(sel1,sbranch1,0,steps1.size()-1,sbranch2);
}

//********************************************************************
void SelectionBase::CopyStep(SelectionBase& sel1, const std::string& sbranch1, UInt_t istep,const std::string& sbranch2){
//********************************************************************

    // Copy only one step
    CopySteps(sel1,sbranch1,istep,istep,sbranch2);
}

//********************************************************************
void SelectionBase::SetValidRunPeriods(std::string runPeriods){
//********************************************************************

    // First, set them all to false, since they are initialised to true
    for(int i = 0; i < 10; ++i){
        _validRunPeriods[i] = false;
    }
    
    // Now, parse the runPeriods string
    std::stringstream ss(runPeriods);
    int i;

    while (ss >> i){
        // If the selection is valid for this run period then set the corresponding array entry to be true
        _validRunPeriods[i] = true;
        // If the next character in the string is a comma, ignore it
        if (ss.peek() == ',') ss.ignore();
    }
}






